﻿/**
* @decription  Main application class for the slide show, controlling loading of XML and navigation.
*/
package com.foed.applications.slideshow {

	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.ui.Keyboard;
	
	import com.foed.applications.slideshow.slides.Slide;
	import com.foed.applications.slideshow.slides.SlideFactory;

	public class SlideShow extends Sprite {
	
		private var _properties:SlideProperties;		
		private var _slideHolder:Sprite;
		private var _model:SlideShowModel;
		private var _slide:Slide;

		/**
		* @description  Constructor.
		*
		* @param  pProperties  The SlideProperties instance that sets default properties for the slide show.
		*/
		public function SlideShow(pProperties:SlideProperties) {
			init(pProperties);
		}
		
		/**
		* @description  Initializes the application by creating the slide holder.
		*
		* @param  pProperties  The SlideProperties instance that sets default properties for the slide show.
		*/
		private function init(pProperties:SlideProperties):void {
			_properties = pProperties;
			_slideHolder = new Sprite();
			addChild(_slideHolder);
		}

		/**
		* @description  Event handler for keyboard input.
		*
		* @param  pEvent  Event fired by Keyboard.
		*/
		private function onKeyDown(pEvent:KeyboardEvent):void {
			if (_slide.locked) return;
			switch (pEvent.keyCode) {
				case Keyboard.PAGE_UP:
				case Keyboard.UP:
				case Keyboard.LEFT:
					if (_slide.hasPreviousSlide()) {
						_slide.displayPreviousSlide();
					}
					break;
				case Keyboard.PAGE_DOWN:
				case Keyboard.SPACE:
				case Keyboard.DOWN:
				case Keyboard.RIGHT:
					if (_slide.hasNextSlide()) {
						_slide.displayNextSlide();
					}
					break;
			}
		}

		/**
		* @description  Event handler for when the slide show model, as defined in the XML, is loaded, parsed and ready to access.
		*
		* @param  pEvent  Event fired by SlideShowModel.
		*/
		private function onModelReady(pEvent:Event):void {
			_model.removeEventListener(Event.COMPLETE, onModelReady);
			_properties.populate(_model.getChild(SlideShowModel.PROPERTIES));
			if (_model.getClassFile()) {
				loadClassFile();
			} else {
				displayFirstSlide();
			}
		}

		/**
		* @description  Event handler for when the SWF file with compiled classes for use with the slide show has loaded.
		*
		* @param  pEvent  Event fired by LoaderInfo.
		*/
		private function onClassFileLoaded(pEvent:Event):void {
			var pLoaderInfo:LoaderInfo = pEvent.target as LoaderInfo;
			pLoaderInfo.removeEventListener(Event.COMPLETE, onClassFileLoaded);
			pLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onClassFileLoadError);
			displayFirstSlide();			
		}
		
		/**
		* @description  Event handler for when their is an error loading SWF file with compiled classes.
		*
		* @param  pEvent  Event fired by LoaderInfo.
		*/
		private function onClassFileLoadError(pEvent:IOErrorEvent):void {
			var pLoaderInfo:LoaderInfo = pEvent.target as LoaderInfo;
			pLoaderInfo.removeEventListener(Event.COMPLETE, onClassFileLoaded);
			pLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onClassFileLoadError);
			trace(pEvent);
		}

		/**
		* @description  Loads a SWF file that contains compiled classes for use with the slide show.
		*/
		private function loadClassFile():void {
			var pClassFile:String = _model.getClassFile().valueOf();
			var pLoader:Loader = new Loader();
			pLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onClassFileLoaded);
			pLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onClassFileLoadError);
			var pContext:LoaderContext = new LoaderContext();
			pContext.applicationDomain = ApplicationDomain.currentDomain;
			try {
				pLoader.load(new URLRequest(pClassFile), pContext);				
			} catch (e:Error) {
				trace(e.getStackTrace());
			}
		}
		
		/**
		* @description  Creates the initial, root slide of the slide show and enables the show for keyboard navigation.
		*/
		private function displayFirstSlide():void {
			_slide = SlideFactory.createSlide(_model.getChild(SlideShowModel.SLIDES), this);
			_slide.reveal();
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
		}
	
		/**
		* @description  Populates the slide show with a model class that contains all the slide information.
		*
		* @param  pModel  The SlideShowModel instance holding all the slide information.
		*/
		public function populate(pModel:SlideShowModel):void {
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			_model = pModel;
			_model.addEventListener(Event.COMPLETE, onModelReady);
			_model.fetchXML();
		}
		
		/**
		* @description  Adds a slide to the slide holder to be viewed.
		*
		* @param  pSlide  The Slide instance to be added for display.
		*/
		public function addSlide(pSlide:Slide):void {
			_slideHolder.addChild(pSlide);
		}
	
		/**
		* @description  Removes a slide from the slide holder.
		*
		* @param  pSlide  The Slide instance to be removed.
		*/
		public function removeSlide(pSlide:Slide):void {
			if (_slideHolder.contains(pSlide)) {
				_slideHolder.removeChild(pSlide);
			}
		}

		/**
		* @description  Returns the slide properties for the slide show.
		*
		* @returns  The slide properties for the slide show.
		*/
		public function get properties():SlideProperties {
			return _properties;
		}
		
		/**
		* @description  Returns the class mappings (i.e., the node names mapped to class names) for the slide show.
		*
		* @returns  The mappings of node names to class names as defined by the model.
		*/
		public function get classMappings():XML {
			return _model.getChild(SlideShowModel.CLASS_MAPPINGS);
		}
		
	}

}